/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.bio;

import java.net.SocketTimeoutException;

import org.zhiqim.httpd.HttpContext;
import org.zhiqim.httpd.websocket.HttpWebSocketAbs;
import org.zhiqim.httpd.websocket.HttpWebSocketService;
import org.zhiqim.kernel.control.Threadx;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.websocket.WebSocketConstants;
import org.zhiqim.kernel.websocket.WebSocketPacket;
import org.zhiqim.kernel.websocket.WebSockets;

/**
 * HttpWebsocket连接线程
 *
 * @version v1.0.0 @author zouzhigang 2017-6-9 新建与整理
 */
public class HttpBioWebSocket extends HttpWebSocketAbs implements WebSocketConstants
{
    private HttpBioConnection conn;
    private WebSocketThread thread;
    
    public HttpBioWebSocket(HttpBioConnection conn, HttpContext context, HashMapSS headers, HashMapSS parameters, String protocol, String sessionId, HttpWebSocketService service)
    {
        super(context, headers, parameters, protocol, sessionId, service);
        
        this.conn = conn;
    }
    
    @Override
    public HttpBioConnection getConnection()
    {
        return conn;
    }
    
    /***********************************************************************************/
    //打开&关闭
    /***********************************************************************************/
    
    @Override
    public synchronized void open()
    {
        if (thread != null)
        {//并发已开启
            return;
        }
        
        thread = new WebSocketThread(this);
        thread.open();
    }
    
    @Override
    public synchronized void close(int code)
    {
        if (thread != null)
        {//1.关闭线程
            thread.close();
            thread = null;
        }
        
        if (conn != null)
        {//2.关闭连接
            conn.close();
            conn = null;
        }
        
        //3.由父类通知业务并从管理器中删除
        super.close(code);
    }
    
    @Override
    public synchronized void exception(Throwable e)
    {
        if (thread != null)
        {//1.关闭线程
            thread.close();
            thread = null;
        }
        
        if (conn != null)
        {//2.关闭连接
            conn.close();
            conn = null;
        }
        
        //3.由父类通知业务并从管理器中删除
        super.exception(e);
    }

    /************************************************************************/
    //监听HTTP升级为WS后的消息
    /************************************************************************/

    private class WebSocketThread extends Threadx
    {
        private HttpBioWebSocket websocket;
        
        public WebSocketThread(HttpBioWebSocket websocket)
        {
            this.websocket = websocket;
        }
        
        @Override
        protected String getThreadName()
        {
            return "Http.WebSocket";
        }
        
        @Override
        protected void loop()
        {
            try
            {
                WebSocketPacket packet = WebSockets.parse(conn.getInputStream());
                switch (packet.getOpcode())
                {
                case PONG:
                {//心跳响响延续会话
                    context.getSessionManager().updateLastAccessed(sessionId);
                    service.onPong(websocket);
                    break;
                }
                case PING:
                {//心跳请求回复响应
                    context.getSessionManager().updateLastAccessed(sessionId);
                    sendPong();
                    service.onPing(websocket);
                    break;
                }
                case CLOSE:
                {//接收到断开，关闭连接
                    websocket.close(packet.getCloseCode());
                    break;
                }
                case TEXT:
                {//接收到文本消息
                    context.getSessionManager().updateLastAccessed(sessionId);
                    service.onText(websocket, packet.getText());
                    break;
                }
                case BIN:
                {//接收到二进制流
                    context.getSessionManager().updateLastAccessed(sessionId);
                    service.onBinary(websocket, packet.getData());
                    break;
                }
                case MID:
                {//接收到中间包
                    context.getSessionManager().updateLastAccessed(sessionId);
                    service.onMiddle(websocket, packet.getData());
                    break;
                }
                default:
                {//最后为其他自定义的报文
                    context.getSessionManager().updateLastAccessed(sessionId);
                    service.onOther(websocket, packet.getData());
                    break;
                }
                }
            }
            catch (SocketTimeoutException e) 
            {//超时继续监听
            }
            catch (Throwable e)
            {//异常要求，交由websocket先关闭监听线程再关闭连接，再通知业务侧，最后从管理器中删除（4步）
                websocket.exception(e);
            }
        }
    }
}
